package com.JackTang.api.utils;

import java.lang.reflect.Field;
import java.sql.*;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * @author Jack Tang
 * @description 封装 dao 数据库重复代码！
 * @create 2023-09-03 22:56
 *
 * TODO:
 *      封装两个方法： 一个简化非DQL
 *                   一个简化DQL
 */
public abstract class BaseDao {
    /**
     * 封装简化非DQL语句
     * @param sql     带占位符的SQL语句
     * @param params  占位符的值  注意，传入占位符的值，必须等于SQL语句 ？ 位置！
     * @return        执行影响的行数
     */
    public int executeUpdate(String sql,Object ...params) throws SQLException {

        //获取连接
        Connection conn = JdbcUtilsV2.getConnection();

        PreparedStatement ps = conn.prepareStatement(sql);
        //占位符赋值
        //可变参数params可以当做数组使用
        for (int i = 1; i <= params.length; i++){
            ps.setObject(i,params[i-1]);
        }
        //6.执行SQL语句 DML类型
        int rows = ps.executeUpdate();

        //空间回收
        ps.close();
        //是否回收连接，需要考虑是不是事务！
        if (conn.getAutoCommit()){
            //没有开启事务
            //没有开启事务，正常回收连接！
            JdbcUtilsV2.freeConnection();
        }

        return rows;
    }

    /**
     * 非DQL语句封装方法 -> 返回值 固定为int
     * DQL语句封装方法 -> 返回值 是什么类型？？？
     *                  并不是 list<Map> map  优点： key 和 value 自定义！不用先设定好！
     *                                       缺点： 没有数据校验机制，也不支持反射操作
     *
     *                  数据库数据 -> java 的实体类
     *
     *                  table
     *                      t_user
     *                          id account password nickname
     *                  java
     *                      User
     *                          id account password nickname
     *
     *                  表中 -> 一行 -> Java类的一个对象 -> 多行 -> List<Java实体类> -> list;
     *      DQL -> List<Map> -> 一行 -> map -> List<Map>
     *      <T> 声明一个泛型，不确定类型
     *      1.确定泛型  User.class T -> User
     *      2.要使用反射技术属性赋值
     *      public <T> List<T> executeQuery(Class<T>,String sql,Object ... params);
     *
     */

    /**
     * 将查询结果封装到一个实体类集合
     * @param clazz    要接值的实体类集合的模板对象
     * @param sql      查询语句，要求列名或者别名等于实体类的属性名
     * @param params   占位符的值  要和 ？ 的位置对象传递
     * @return 查询的实体类集合
     * @param <T>  声明的结果的类型
     * @throws SQLException
     * @throws InstantiationException
     * @throws IllegalAccessException
     * @throws NoSuchFieldException
     */
    public <T> List<T> executeQuery(Class<T> clazz, String sql, Object ... params) throws SQLException, InstantiationException, IllegalAccessException, NoSuchFieldException {
        //获取连接
        Connection conn = JdbcUtilsV2.getConnection();

        //创建ps
        PreparedStatement ps = conn.prepareStatement(sql);

        //占位符填充
        if (params != null && params.length != 0){
            for (int i = 0; i < params.length; i++) {
                ps.setObject(i,params[i]);
            }
        }

        //获取执行后返回的结果
        ResultSet resultSet = ps.executeQuery();

        //结果集解析


        //创建一个集合
        List<T> mapList = new ArrayList<>();

        //获取列表信息，包括列名，列的类型等
        ResultSetMetaData metaData = resultSet.getMetaData();
        //获取了结果集 metaData 的列数
        int columnCount = metaData.getColumnCount();
        while (resultSet.next()) {
            //每行数据 对应一个 T 类型的对象
            T t = clazz.newInstance(); //调用类的无参构造函数实例化对象
            // 注意： 要从 1 开始，并且小于等于总列数
            for (int i = 1; i <= columnCount; i++) {
                //对象的属性值
                Object value = resultSet.getObject(i);
                //获取指定列下角标的列的名称！ResultSetMetaData
                String propertyName = metaData.getColumnLabel(i);
                //反射，给对象的属性值赋值
                Field field = clazz.getDeclaredField(propertyName);
                //由于反射获取的这个属性是私有的，则需要将其属性设置为可以访问的
                field.setAccessible(true); //属性可以设置，打破private的修饰限制
                /**
                 * 参数1 ： 要赋值的对象  如果属性时静态，第一个参数可以为null！
                 * 参数2 ： 具体的属性值
                 */
                field.set(t,value);


            }
            mapList.add(t);
        }
        //关闭资源
        resultSet.close();
        ps.close();
        if (conn.getAutoCommit()){
            //没有开启事务
            //没有开启事务，正常回收连接！
            JdbcUtilsV2.freeConnection();
        }
        return mapList;
    }
}